package com.itcoon.transform.starter.proxy;


import com.itcoon.common.utils.SpringUtils;
import com.itcoon.transform.base.constants.TransformConstants;
import com.itcoon.transform.base.proxy.IProxy;
import com.itcoon.transform.base.utils.TransformServiceUtils;
import com.itcoon.transform.inject.InjectHandler;
import com.itcoon.transform.assemble.assembler.inter.MethodAssemblerComposite;
import com.itcoon.transform.base.definition.InjectDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class TransForm4jJdkProxy<T, R> implements InvocationHandler, IProxy<T, R> {

    private static final Logger log = LoggerFactory.getLogger(TransForm4jJdkProxy.class);
    private static final MethodAssemblerComposite ASSEMBLER_COMPOSITE = SpringUtils.getBean(MethodAssemblerComposite.class);
    private static final InjectHandler INJECT_HANDLER = SpringUtils.getBean(InjectHandler.class);

    private final Class<R> targetClass;

    private final T source;

    private final List<InjectDefinition.InjectInfo> injectInfos;

    public TransForm4jJdkProxy(Class<R> targetClass, T source, List<InjectDefinition.InjectInfo> injectInfos) {
        this.targetClass = targetClass;
        this.source = source;
        this.injectInfos = injectInfos;
    }

    @Override
    public <RR extends R> RR instance() {
        Class<?>[] interfaces = targetClass.getInterfaces();
        Class<?>[] finalInterfaces = new Class[interfaces.length + 2];
        finalInterfaces[0] = targetClass;
        finalInterfaces[1] = IProxy.class;
        for (int i = 0; i < interfaces.length; i++) {
            finalInterfaces[i + 2] = interfaces[i];
        }
        return (RR) Proxy.newProxyInstance(targetClass.getClassLoader(), finalInterfaces, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (!method.getName().startsWith(TransformConstants.GETTER_PREFIX)) {
            return method.invoke(this, args);
        }
        //编辑前先过滤injectInfos
        List<InjectDefinition.InjectInfo> methodInjectInfos = new ArrayList<>();
        for (InjectDefinition.InjectInfo injectInfo : injectInfos) {
            String fullAttribute = injectInfo.getAttribute();
            String methodAttribute = TransformServiceUtils.formatDataValue(method.getName());
            if(fullAttribute.contains(".")){
                String firstData = fullAttribute.substring(0, fullAttribute.indexOf("."));
                if(firstData.equals(methodAttribute)){
                    String attribute = fullAttribute.substring(fullAttribute.indexOf(".") + 1);
                    InjectDefinition.InjectInfo info = new InjectDefinition.InjectInfo(attribute, injectInfo.getInjectMode(), injectInfo.getAttributeSource());
                    methodInjectInfos.add(info);
                }
            }
        }
        // 进行编集： 1. 默认方法 2. 其他普通 copy 式普通编集
        Object assembleResult = ASSEMBLER_COMPOSITE.assemble(source, targetClass, proxy, method, methodInjectInfos, args);
        log.debug("Assemble targetMethod#{} from source#{} to target#{} successfully, pending to inject", method.getName(), source, targetClass.getSimpleName());
        // 进行注入
        InjectDefinition.InjectInfo injectInfo = INJECT_HANDLER.support(method, injectInfos);
        return injectInfo == null ? assembleResult : INJECT_HANDLER.handle(targetClass, injectInfo, assembleResult);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TransForm4jJdkProxy<?, ?> that = (TransForm4jJdkProxy<?, ?>) o;
        return targetClass.equals(that.targetClass) &&
                source.equals(that.source) &&
                injectInfos.equals(that.injectInfos);
    }

    @Override
    public int hashCode() {
        return Objects.hash(targetClass, source, injectInfos);
    }
}
